// import _ from 'lodash'
var window
const _ = (window && window._) || {
    curry: function(fWithTwoArguments) {
        return function(x) {
            return function(y) {
                return fWithTwoArguments(x, y)
            }
        }
    },
    flowRight: function(f1, f2) {
        return function(...args) {
            return f1(f2(...args))
        }
    }
}
export const lodash = _

const Maybe = function(value) {
    this.__value = value
}
Maybe.of = function(x) {return new Maybe(x)}
Maybe.prototype.isNothing = function() {
    return (this.__value === undefined) || (this.__value === null)
}
Maybe.prototype.map = function(f) {
    return this.isNothing() ? Maybe.of(null) : Maybe.of(f(this.__value))
}
Maybe.prototype.join = function() {
    return this.isNothing() ? Maybe.of(null) : this.__value
}
Maybe.prototype.chain = function(f) {
    return this.map(f).join()
}
Maybe.prototype.ap = function(otherContainer) {
    return otherContainer.map(this.__value)
}

const map = _.curry((f, functor) => {
    return functor.map(f)
})
const join = (functor) => {
    return functor.join()
}
const chain = _.curry((f, functor) => {
    return functor.chain(f)
})
const add = _.curry((x, y) => x + y)

const IO = function(f) {
    this.__value = f
}
IO.of = function(x) {return new IO(function() {
    return x
})}
IO.prototype.map = function(f) {
    return new IO(_.flowRight(f, this.__value))
}
IO.prototype.ap = function(functor) {
    return functor.map(this.__value)
}
IO.prototype.unsafePerform = function() {
    return this.__value()
}
IO.prototype.join = function() {
    return this.unsafePerform()
}


export {
    Maybe,
    IO,
    map,
    join,
    chain,
    add,
}